package han.cirno.corrupt.components.school.impl.mju

import han.cirno.corrupt.Application
import han.cirno.corrupt.R
import han.cirno.corrupt.components.school.SchoolProvider
import han.cirno.corrupt.components.school.class_time.ClassTime
import han.cirno.corrupt.components.school.class_time.SimpleClassTime
import han.cirno.corrupt.components.school.term_time.BasicTermTime
import han.cirno.corrupt.components.school.term_time.TermTime
import han.cirno.corrupt.data.DB
import han.cirno.corrupt.data.SettingKeys
import han.cirno.corrupt.data.instances.UserClassesData
import han.cirno.corrupt.util.ViewUtil
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.Calendar

class MjuSchool: SchoolProvider {
    companion object{
        const val KEY_SESSION_ID="key_mju:session-id"
        val supportedInfo= arrayOf(
            SchoolProvider.INFO_EXAMS,
            SchoolProvider.INFO_CLASSES,
            SchoolProvider.INFO_SCORE
        )
        val mjuClassTime= SimpleClassTime.Builder()
            .add(8,30)
            .add(9,25)
            .add(10,30)
            .add(11,25)
            .add(14,0)
            .add(14,55)
            .add(15,50)
            .add(16,45)
            .add(18,30)
            .add(19,25)
            .build()
        val termTime= BasicTermTime()
    }

    override fun getSchoolName(): String = "Minjiang University"
    override fun getClassTime(): ClassTime = mjuClassTime
    override fun getTermTime(): TermTime = termTime
    override fun hasInfo(infoKey: String): Boolean = supportedInfo.contains(infoKey)

    override fun getString(key: String): String? {
        return when(key){
            SchoolProvider.STRING_INFO -> {
                val dataOperation = dataOperation()
                ViewUtil.getString(
                    resId = R.string.card_personalInfo_logged_mju,
                    paras = arrayOf(
                        getSchoolName(),
                        dataOperation.singleGet(SchoolProvider.KEY_USERNAME,String::class)?:"",
                        dataOperation.singleGet(SchoolProvider.KEY_ID,String::class)?:""))
            }
            else->null
        }
    }

    override suspend fun performLogin(
        userName: String,
        password: String,
        callback: SchoolProvider.LoginCallback
    ): Result<String> {
        dataOperation()
            .write(SchoolProvider.KEY_ID, userName)
            .write(SchoolProvider.KEY_PASSWORD, password)
            .write(KEY_SESSION_ID,"(Invalid)")
            .save()
        return MjuEduClient.instance.request(null,callback)
    }

    override suspend fun fetchInfo(infoKey: String, vararg params: Any?): Result<Any> {
        return try{when (infoKey) {
            SchoolProvider.INFO_CLASSES -> fetchClassesTable()
            SchoolProvider.INFO_EXAMS -> fetchExamInfo()
            SchoolProvider.INFO_SCORE->fetchScoreInfo()
            else -> Result.failure(IllegalArgumentException("Not a valid info key!"))
        }}catch (e:Exception){
            e.printStackTrace()
            Result.failure(e)
        }
    }

    private fun fetchScoreInfo():Result<Unit>{
        var xnm=""
        var xqm=""
        MjuEduClient.instance.request(MjuEduConst.Payload.Score)
            .onSuccess {
                try {
                    if (it.isEmpty()) {
                        return Result.failure(Exception("Empty response body"))
                    }
                    val classPageDoc = Jsoup.parse(it)
                    val xnmElement = classPageDoc.getElementById("xnm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    val xqmElement = classPageDoc.getElementById("xqm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    xnm = xnmElement.attr("value")
                    xqm = xqmElement.attr("value")
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        MjuEduClient.instance.request(MjuEduConst.Payload.ScoreData(xnm, xqm))
            .onSuccess {
                val scoreDao= DB.getInstance().scoreDao()
                scoreDao.deleteAll()
                scoreDao.insertAll(MjuScoreResolver.analyse(it))
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }

    private fun fetchExamInfo():Result<Unit>{
        var xnm=""
        var xqm=""
        MjuEduClient.instance.request(MjuEduConst.Payload.Exam)
            .onSuccess {
                try {
                    if (it.isEmpty()) {
                        return Result.failure(Exception("Empty response body"))
                    }
                    val classPageDoc = Jsoup.parse(it)
                    val xnmElement = classPageDoc.getElementById("cx_xnm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    val xqmElement = classPageDoc.getElementById("cx_xqm")!!
                        .getElementsByAttributeValue("selected", "selected")
                    xnm = xnmElement.attr("value")
                    xqm = xqmElement.attr("value")
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        MjuEduClient.instance.request(MjuEduConst.Payload.ExamData(xnm, xqm))
            .onSuccess {
                val examDao= DB.getInstance().examDao()
                try{
                    examDao.deleteAll()
                    examDao.insertAll(MjuExamResolver.analyze(it))
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }

    private fun fetchClassesTable():Result<Any>{
        var xnm=""
        var xqm=""
        var xnmReadable=""
        var xqmReadable=""
        val classTableDao= DB.getInstance().classTableDao()
        val classEntryDao= DB.getInstance().classEntryDao()
        MjuEduClient.instance.request(MjuEduConst.Payload.ClassesTable)
            .onSuccess{
                if (it.isEmpty()){
                    return Result.failure(Exception("Empty response body"))
                }
                val (xnd,xqd)=getTermTime().getTerms(Calendar.getInstance())
                val classPageDoc = Jsoup.parse(it)
                val xnmElement = classPageDoc.getElementById("xnm")!!
                val xqmElement = classPageDoc.getElementById("xqm")!!
                var xnmElements: Element
                var xqmElements:Element
                when(Application.application.sharedPreferences.getString(SettingKeys.SELECT_DEFAULT_GETTER_METHOD,"0")){
                    "0"->{
                        try {
                            xnmElements = xnmElement.getElementsMatchingText(xnd).elementAt(1)
                            xqmElements = xqmElement.getElementsMatchingText(xqd).elementAt(1)
                        }catch (e:Exception){
                            xnmElements=xnmElement.getElementsByAttributeValue("selected","selected").elementAt(0)
                            xqmElements=xqmElement.getElementsByAttributeValue("selected","selected").elementAt(0)
                        }
                    }
                    else->{
                        xnmElements=xnmElement.getElementsByAttributeValue("selected","selected").elementAt(0)
                        xqmElements=xqmElement.getElementsByAttributeValue("selected","selected").elementAt(0)
                    }
                }
                xnm = xnmElements.attr("value")
                xqm = xqmElements.attr("value")
                xnmReadable=xnmElements.text()
                xqmReadable=xqmElements.text()
            }.onFailure {
                return Result.failure(it)
            }
        MjuEduClient.instance.request(MjuEduConst.Payload.ClassTableData(xnm, xqm))
            .onSuccess {
                UserClassesData.makeDirty()
                classTableDao.deleteAll()
                classEntryDao.deleteAll()
                try{
                    val classTable = MjuClassResolver.analyze(it, xnmReadable, xqmReadable)
                    classTableDao.insert(classTable)
                    classEntryDao.insertAll(classTable.classEntries)
                }catch (e:Exception){
                    e.printStackTrace()
                    return Result.failure(e)
                }
            }.onFailure {
                return Result.failure(it)
            }
        return Result.success(Unit)
    }
}